import numpy

from classfile.attributes import Code, LineNumberTable
from classfile.member import Field, ClassMember
from heap.access_flag import AccessFlagHelper
from heap.class_klass import Klass
from heap.exception import ExceptionHandler


class ClassMember:
    def __init__(self):
        # 访问标志
        self.accessFlags = None
        # 名称
        self.name = None
        # 描述符
        self.descriptor = None
        # 签名
        self.signature = None
        # 注解数据
        self.annotationData = []
        # 类  filed或者method 所在的类
        self.klass = None

    def setAccessFlags(self, accessFlags):
        self.accessFlags = accessFlags

    def setName(self, name):
        self.name = name

    def setDescriptor(self, descriptor):
        self.descriptor = descriptor

    def setSignature(self, signature):
        self.signature = signature

    def setAnnotationData(self, annotationData):
        self.annotationData = annotationData

    def setKlass(self, klass):
        self.klass = klass

    def getAccessFlags(self):
        return self.accessFlags

    def getName(self):
        return self.name

    def getDescriptor(self):
        return self.descriptor

    def getSignature(self):
        return self.signature

    def getAnnotationData(self):
        return self.annotationData

    def getKlass(self):
        return self.klass

    def isAccessibleTo(self, d: Klass) -> bool:
        if AccessFlagHelper.isPublic(self.accessFlags):
            return True
        c = self.klass
        if c == d:
            return True
        if AccessFlagHelper.isProtected(self.accessFlags):
            return d.isSubClassOf(c) or d.getPackageName() == c.getPackageName()
        if not AccessFlagHelper.isPrivate(self.accessFlags):
            return d.getPackageName() == c.getPackageName()
        return False

    def copyMemberInfo(self, m: ClassMember):
        self.accessFlags = m.accessFlags
        self.name = m.getName()
        self.descriptor = m.getDescriptor()

    def isStatic(self):
        return AccessFlagHelper.isStatic(self.accessFlags)

    def isFinal(self):
        return AccessFlagHelper.isFinal(self.accessFlags)

    def isNative(self):
        return AccessFlagHelper.isNative(self.accessFlags)


class Field(ClassMember):
    def __init__(self):
        super().__init__()
        self.slotId = None
        self.slotCount = 1
        self.constantValueIndex: numpy.uint16 = 0

    def setSlotId(self, slotId):
        self.slotId = slotId

    def setConstantValueIndex(self, f):
        cv = f.getConstantValue()
        if cv is not None:
            self.constantValueIndex = cv.getConstantValueIndex()

    def copyAttributes(self, f):
        self.setConstantValueIndex(f)

    def getSlotId(self):
        return self.slotId

    def setSlotCount(self, slotCount: int):
        self.slotCount = slotCount

    def isLongOrDouble(self):
        return self.descriptor in ["J", "D"]


class Method(ClassMember):
    def __init__(self):
        super().__init__()
        self.slotId = 0
        self.code = None
        self.maxStack = 0
        self.maxLocals = 0
        self.exceptionTable = []
        self.lineNumberTable = None
        self.methodDescriptor = None

    def setSlotId(self, slotId):
        self.slotId = slotId

    def setCode(self, code):
        self.code = code

    def getSlotId(self):
        return self.slotId

    def getCode(self):
        return self.code

    # 解析方法的描述符
    def parseDescriptor(self, descriptor):
        self.methodDescriptor = MethodDescriptor(descriptor)
        return self.methodDescriptor

    # 判断方法是否是native
    def isNative(self):
        return AccessFlagHelper.isNative(self.accessFlags)

    # 计算方法的本地变量表和操作数栈的大小
    def calcNativeStackAndLocals(self):
        rt = self.methodDescriptor.getReturnType()
        self.maxLocals = self.slotId
        self.maxStack = 4
        if rt[0] == 'V':  # void return
            self.code = [numpy.uint8(0xFE), numpy.uint8(0xB1)]  # return
        elif rt[0] == 'D':  # dreturn
            self.code = [numpy.uint8(0xFE), numpy.uint8(0xaf)]  # return
        elif rt[0] == 'F':  # freturn
            self.code = [numpy.uint8(0xFE), numpy.uint8(0xae)]  # return
        elif rt[0] == 'J':  # lreturn
            self.code = [numpy.uint8(0xFE), numpy.uint8(0xad)]  # return
        elif rt[0] == 'L':  # areturn
            self.code = [numpy.uint8(0xFE), numpy.uint8(0xb0)]  # return
        else:
            # ireturn
            self.code = [numpy.uint8(0xFE), numpy.uint8(0xac)]  # return

    def calcParameterSlotIds(self):
        if self.methodDescriptor is not None:
            if self.methodDescriptor.getParameterCount() > 0:
                for parameterType in self.methodDescriptor.getParameterTypes():
                    if parameterType == "J" or parameterType == "D":
                        self.slotId += 2
                    else:
                        self.slotId += 1

        # 非静态方法的第一个slot是this
        if not self.isStatic():
            self.slotId += 1

    # 拷贝方法字节码指令
    def copyAttributes(self, m):
        code: Code = m.getCode()
        if code is not None:
            self.maxStack = int(code.getMaxStack())
            self.maxLocals = int(code.getMaxLocals())
            self.code = code.bytes()  # 这里需要深拷贝
            et = code.getExceptionTable()
            if et is not None:
                if len(et) > 0:
                    for exception in et:
                        self.exceptionTable.append(ExceptionHandler().parse(exception, self.klass.constantPool))

            self.lineNumberTable: LineNumberTable = code.getLineNumberTable()

    def findExceptionHandler(self, exceptionType, pc):

        for exceptionHandler in self.exceptionTable:
            if pc >= exceptionHandler.getStartPc() and pc < exceptionHandler.getEndPc():
                if exceptionHandler.getCatchType() == None:
                    if exceptionHandler is not None:
                        return exceptionHandler.getHandlerPc()
                    else:
                        return -1
                catchType = exceptionHandler.getCatchType().resolveClass()
                if catchType == exceptionType or catchType.isSuperClassOf(exceptionType):
                    if exceptionHandler is not None:
                        return exceptionHandler.getHandlerPc()
                    else:
                        return -1
        return -1


class MethodDescriptor:
    def __init__(self, descriptor):
        self.descriptor = descriptor
        self.parameterTypes = []
        self.returnType = None
        self.parse()

    def parse(self):
        if self.descriptor is None:
            return
        if self.descriptor.startswith("("):
            index = 1
            while index < len(self.descriptor):
                if self.descriptor[index] == ")":
                    break
                if self.descriptor[index] == "L":
                    endIndex = self.descriptor.find(";", index)
                    if endIndex == -1:
                        raise Exception("Invalid descriptor: " + self.descriptor)
                    self.parameterTypes.append(self.descriptor[index:endIndex + 1])
                    index = endIndex + 1
                elif self.descriptor[index] == "[" and self.descriptor[index + 1] == "L":
                    endIndex = self.descriptor.find(";", index)
                    if endIndex == -1:
                        raise Exception("Invalid descriptor: " + self.descriptor)
                    self.parameterTypes.append(self.descriptor[index:endIndex + 1])
                    index = endIndex + 1
                elif self.descriptor[index] == "[":
                    self.parameterTypes.append(self.descriptor[index:index + 1])
                    index += 2
                else:
                    self.parameterTypes.append(self.descriptor[index])
                    index += 1
            if index >= len(self.descriptor):
                raise Exception("Invalid descriptor: " + self.descriptor)
            if self.descriptor[index] == ")":
                index += 1
            if self.descriptor[index] == "V":
                self.returnType = "void"
            elif self.descriptor[index] == "L":
                endIndex = self.descriptor.find(";", index)
                if endIndex == -1:
                    raise Exception("Invalid descriptor: " + self.descriptor)
                self.returnType = self.descriptor[index:endIndex + 1]
                index = endIndex + 1
            else:
                self.returnType = self.descriptor[index]
                index += 1
        else:
            raise Exception("Invalid descriptor: " + self.descriptor)

    def getParameterTypes(self):
        return self.parameterTypes

    def getReturnType(self):
        return self.returnType

    def getParameterCount(self):
        return len(self.parameterTypes)

    def getMethodDescriptor(self):
        return self.descriptor  # 这里返回的是方法描述符
